package projectAnalysis;

import org.apache.maven.model.Dependency;
import projectAnalysis.advancedTechAnalyzer.*;

import java.io.File;
import java.util.*;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

/**
 * 高级技术分析器（支持多特征匹配、模糊分类）
 * 功能：根据依赖分析项目技术栈，支持多特征匹配、模糊分类。
 *  1. 多特征匹配：根据groupId、artifactId、版本号等多维度匹配技术特征。
 *  2. 模糊分类：未匹配到特征的依赖，根据groupId前缀进行模糊分类。
 *
 *  使用方法：
 *   修改main方法中的projectPath为目标项目路径。
 */
public class AdvancedTechAnalyzer {
    // 技术特征库（从JSON加载）
    private List<TechDefinition> techDefinitions;
    // 技术-置信度分数映射（特征匹配的技术）
    private Map<String, Integer> techScores = new HashMap<>();
    // 原始依赖列表（用于模糊识别未配置的技术）
    private List<String> rawDependencies = new ArrayList<>();
    // 基础匹配分数（不同特征的权重）
    private static final int BASE_SCORE = 30;

    // 构造方法：加载技术特征库
    public AdvancedTechAnalyzer() throws Exception {
        this.techDefinitions = new TechFeatureLoader().loadFromResource();
        System.out.println("已加载技术特征数量：" + techDefinitions.size());
    }

    // 核心分析方法：入口
    public void analyze(String projectPath) throws Exception {
        File projectDir = new File(projectPath);
        if (!projectDir.exists()) {
            throw new RuntimeException("项目路径不存在：" + projectPath);
        }

        // 初始化进度输出
        System.out.println("\n===== 开始分析项目技术栈 =====");
        System.out.println("目标项目路径：" + projectDir.getAbsolutePath());

        // 1. 解析Maven依赖（直接依赖+可选传递依赖）
        analyzeMavenDependencies(projectDir);

        // 2. 解析Java源码（注解、接口实现）
        System.out.println("\n===== 开始扫描Java源码 =====");
        analyzeJavaSources(projectDir);
        System.out.println("===== 完成Java源码扫描 =====");

        // 3. 解析配置文件（application.yml、mybatis-config.xml等）
        System.out.println("\n===== 开始扫描配置文件 =====");
        analyzeConfigFiles(projectDir);
        System.out.println("===== 完成配置文件扫描 =====");

        // 4. 解析JAR包（lib目录或target目录下的JAR）
        System.out.println("\n===== 开始扫描JAR包 =====");
        analyzeJarFiles(projectDir);
        System.out.println("===== 完成JAR包扫描 =====");

        // 5. 对未匹配到特征的依赖进行模糊识别
        Map<String, List<String>> fuzzyTechs = TechFuzzyClassifier.classifyUnmatchedDependencies(rawDependencies);

        // 输出最终分析结果
        printResult(fuzzyTechs);
    }

    // 分析Maven依赖（直接依赖+传递依赖）
    private void analyzeMavenDependencies(File projectDir) throws Exception {
        System.out.println("\n===== 开始解析Maven依赖 =====");
        MavenDependencyAnalyzer mavenAnalyzer = new MavenDependencyAnalyzer();

        // 解析直接依赖
        List<Dependency> directDependencies = mavenAnalyzer.parseDirectDependencies(projectDir);
        for (Dependency dep : directDependencies) {
            String groupArtifact = dep.getGroupId() + ":" + dep.getArtifactId();
            // 收集原始依赖（用于模糊识别）
            rawDependencies.add(groupArtifact);
            // 匹配groupId特征，累加分数
            matchFeature(groupArtifact, "groupIdPatterns", BASE_SCORE);
        }

        // 可选：解析传递依赖（取消注释并填写Maven路径即可启用）
        // 注意：首次执行可能因下载依赖较慢，实时日志会显示进度
        String mavenBinPath = "你的Maven路径/bin"; // 例如："D:/apache-maven-3.6.3/bin"
        mavenAnalyzer.parseTransitiveDependencies(projectDir, mavenBinPath);
        System.out.println("===== 完成Maven依赖解析 =====");
    }

    // 递归扫描Java源码（提取注解、接口实现）
    private void analyzeJavaSources(File dir) {
        if (!dir.exists()) {
            System.out.println("跳过不存在的目录：" + dir.getAbsolutePath());
            return;
        }

        File[] files = dir.listFiles();
        if (files == null) {
            System.out.println("无法访问目录：" + dir.getAbsolutePath());
            return;
        }

        for (File file : files) {
            if (file.isDirectory()) {
                // 递归扫描子目录
                analyzeJavaSources(file);
            } else if (file.getName().endsWith(".java")) {
                // 解析单个Java文件
                System.out.println("解析Java文件：" + file.getAbsolutePath());
                JavaSourceAnalyzer javaAnalyzer = new JavaSourceAnalyzer();

                // 提取注解并匹配特征
                List<String> annotations = javaAnalyzer.extractAnnotations(file);
                for (String anno : annotations) {
                    matchFeature(anno, "annotationPatterns", BASE_SCORE + 10); // 注解权重稍高
                }

                // 提取实现的接口并匹配特征
                List<String> interfaces = javaAnalyzer.extractInterfaces(file);
                for (String iface : interfaces) {
                    matchFeature(iface, "classNamePatterns", BASE_SCORE);
                }
            }
        }
    }

    // 递归扫描配置文件（匹配配置文件名/内容）
    private void analyzeConfigFiles(File dir) {
        if (!dir.exists()) {
            System.out.println("跳过不存在的目录：" + dir.getAbsolutePath());
            return;
        }

        File[] files = dir.listFiles();
        if (files == null) {
            System.out.println("无法访问目录：" + dir.getAbsolutePath());
            return;
        }

        for (File file : files) {
            if (file.isDirectory()) {
                // 递归扫描子目录
                analyzeConfigFiles(file);
            } else {
                // 匹配配置文件名（如application.yml、logback.xml）
                String fileName = file.getName();
                System.out.println("检查配置文件：" + fileName);
                matchFeature(fileName, "configPatterns", BASE_SCORE);

                // 简单匹配配置文件内容（仅示例，可扩展）
                if (fileName.endsWith(".properties") || fileName.endsWith(".yml")) {
                    // 实际项目可添加内容解析逻辑（如读取spring.datasource.url）
                }
            }
        }
    }

    // 递归扫描JAR包（匹配JAR文件名）
    private void analyzeJarFiles(File dir) {
        if (!dir.exists()) {
            System.out.println("跳过不存在的目录：" + dir.getAbsolutePath());
            return;
        }

        File[] files = dir.listFiles();
        if (files == null) {
            System.out.println("无法访问目录：" + dir.getAbsolutePath());
            return;
        }

        for (File file : files) {
            if (file.isDirectory()) {
                // 递归扫描子目录（重点关注lib和target目录）
                if (file.getName().equals("lib") || file.getName().equals("target")) {
                    System.out.println("进入JAR包目录：" + file.getAbsolutePath());
                    analyzeJarFiles(file);
                } else {
                    analyzeJarFiles(file); // 其他目录也扫描，但优先级低
                }
            } else if (file.getName().endsWith(".jar")) {
                // 匹配JAR文件名特征
                String jarName = file.getName();
                System.out.println("检查JAR包：" + jarName);
                matchFeature(jarName, "jarNamePatterns", BASE_SCORE - 10); // JAR权重稍低（可能是间接依赖）
            }
        }
    }

    // 核心匹配逻辑：根据特征类型匹配正则，累加技术分数
    private void matchFeature(String target, String featureType, int score) {
        for (TechDefinition tech : techDefinitions) {
            List<String> patterns = getPatternsByType(tech, featureType);
            for (String pattern : patterns) {
                if (Pattern.matches(pattern, target)) {
                    // 累加分数（相同技术多次匹配会累积）
                    int currentScore = techScores.getOrDefault(tech.getName(), 0);
                    techScores.put(tech.getName(), currentScore + score);
                    // 输出匹配日志（可选，用于调试）
                    // System.out.printf("匹配到技术：%s（特征：%s，+%d分）%n", tech.getName(), target, score);
                }
            }
        }
    }

    // 根据特征类型获取对应的正则列表（兼容JDK 8的switch）
    private List<String> getPatternsByType(TechDefinition tech, String featureType) {
        TechDefinition.TechFeatures features = tech.getFeatures();
        List<String> result = Collections.emptyList();
        switch (featureType) {
            case "groupIdPatterns":
                result = features.getGroupIdPatterns();
                break;
            case "annotationPatterns":
                result = features.getAnnotationPatterns();
                break;
            case "classNamePatterns":
                result = features.getClassNamePatterns();
                break;
            case "configPatterns":
                result = features.getConfigPatterns();
                break;
            case "jarNamePatterns":
                result = features.getJarNamePatterns();
                break;
            default:
                result = Collections.emptyList();
        }
        // 避免空指针（配置中未定义该特征时返回空列表）
        return result == null ? Collections.emptyList() : result;
    }

    // 打印最终分析结果（特征匹配的技术 + 模糊识别的技术）
    private void printResult(Map<String, List<String>> fuzzyTechs) {
        System.out.println("\n\n===== 项目技术栈分析结果 =====");

        // 1. 输出特征匹配的技术（置信度>50分）
        System.out.println("\n【1. 特征匹配的技术（置信度>50）】");
        List<Map.Entry<String, Integer>> sortedTechs = techScores.entrySet().stream()
                .filter(entry -> entry.getValue() > 50)
                .sorted((e1, e2) -> e2.getValue().compareTo(e1.getValue())) // 按分数降序
                .collect(Collectors.toList());

        if (sortedTechs.isEmpty()) {
            System.out.println("未识别到明确匹配特征的技术（可扩展tech-features.json增加特征）");
        } else {
            for (Map.Entry<String, Integer> entry : sortedTechs) {
                System.out.printf("- %s（置信度：%d%%）%n", entry.getKey(), entry.getValue());
            }
        }

        // 2. 输出模糊识别的技术（配置中未定义，但依赖中存在）
        System.out.println("\n【2. 模糊识别的技术（配置中未定义）】");
        if (fuzzyTechs.isEmpty()) {
            System.out.println("未识别到额外技术");
        } else {
            for (Map.Entry<String, List<String>> entry : fuzzyTechs.entrySet()) {
                System.out.printf("- %s：%s%n", entry.getKey(),
                        entry.getValue().stream().limit(5).collect(Collectors.joining(", ")) +
                                (entry.getValue().size() > 5 ? "..." : "")); // 最多显示5个依赖
            }
        }

        System.out.println("\n===== 分析完成 =====");
    }

    // 主方法：运行入口
    public static void main(String[] args) {
        try {
            // 替换为你的项目根路径（例如："D:/Project/你的项目"）
            String projectPath = "D:\\Project\\gitee\\ycjz\\finance-management";
            new AdvancedTechAnalyzer().analyze(projectPath);
        } catch (Exception e) {
            System.out.println("分析过程出错：" + e.getMessage());
            e.printStackTrace();
        }
    }
}